package com.proyecto.core.Utilidades;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.StringTokenizer;

public abstract class UtilReflect {
	public static final boolean ES_METODO_GET = true;
	public static final boolean ES_METODO_SET = false;
	
	static final public Object invocarMetodoGet(Object  pObjeto,
									            String  pPropiedad,
									            Class   pParametros[]) throws Exception {
		return invocarMetodoGetSet(ES_METODO_GET, pObjeto, pPropiedad,  pParametros, null);
	}

	static final public void invocarMetodoSet(Object  pObjeto,
											  String  pPropiedad,
											  Class   pParametros[],
											  Object  pValor) throws Exception {
		invocarMetodoGetSet(ES_METODO_SET, pObjeto, pPropiedad, pParametros, pValor);
	}
	
	static final public void invocarMetodoSet(Object  pObjeto,
			  String  pPropiedad,
			  Class   pParametros[],
			  Object  pValor[]) throws Exception {
		invocarMetodoGetSet(ES_METODO_SET, pObjeto, pPropiedad, pParametros, pValor);
	}
	
	static final public Object[] obtenerMetodoGet(Object  pObjeto,
												String  pPropiedad,
									            Class   pParametros[]) throws Exception {
		return obtenerMetodoGetSet(ES_METODO_GET, pObjeto, pPropiedad,  pParametros);
	}
	
	static final public Object[] obtenerMetodoSet(Object  pObjeto,
												String  pPropiedad,
									            Class   pParametros[]) throws Exception {
		return obtenerMetodoGetSet(ES_METODO_SET, pObjeto, pPropiedad, pParametros);
	}
	
	static final public Object invocarMetodoGetSet(boolean pEsMetodoGet,
											  Object  pObjeto,
											  String  pPropiedad, 
											  Class   pParametros[],
											  Object  pValor) throws Exception {
		Object[] elementos = obtenerMetodoGetSet(pEsMetodoGet, pObjeto, pPropiedad, pParametros);
		Object objeto = elementos[0];
		Method metodo = (Method) elementos[1];
		if (pEsMetodoGet){
			if(objeto!=null){
				return metodo.invoke(objeto);
			}else{
				return null;
			}
			
		}else{
			return metodo.invoke(objeto, pValor);
		}
	}
	
	/**
	 * Este metodo devuelve un Object[] de 2 posiciones donde la primera posicion corresponde al objeto y el segundo
	 * al metodo solicitado del objeto
	 * @param pEsMetodoGet
	 * @param pObjeto
	 * @param pPropiedad
	 * @param pParametros
	 * @return
	 * @throws Exception
	 */
	static final private Object[] obtenerMetodoGetSet(boolean pEsMetodoGet,
								   			  		  Object  pObjeto,
								   			  		  String  pPropiedad, 
								   			  		  Class   pParametros[]) throws Exception {
		Method metodo = null;
		String metodoNombre;
		
		if (pEsMetodoGet) 
			metodoNombre = "get";
		else metodoNombre = "set";
		//------------
		StringTokenizer propiedadCompuesta = new StringTokenizer(pPropiedad,".");
		String propiedadToken = (String) propiedadCompuesta.nextElement();
		Object objeto = pObjeto;
		Object objetoCompuesto;
		while(propiedadCompuesta.hasMoreElements()){
			objetoCompuesto = invocarMetodoGetSet(ES_METODO_GET, objeto, propiedadToken, new Class[] {},"");
			propiedadToken = (String) propiedadCompuesta.nextElement();
			objeto = objetoCompuesto;
		}
		//---------------
		metodoNombre = metodoNombre + UtilConversion.inicialMayuscula(propiedadToken);
		if(objeto!=null){
			metodo = objeto.getClass().getDeclaredMethod((String) metodoNombre, pParametros);
		}
		
		return new Object[] {objeto,metodo};
	}
	
	
	static final public Object invocarMetodo(Object  pObjeto,
											 String  pPropiedad, 
											 Class   pParametros[],
											 Object  pValor) throws Exception {
		Object[] elementos = obtenerMetodo(pObjeto, pPropiedad, pParametros);
		Object objeto = elementos[0];
		Method metodo = (Method) elementos[1];
		if (pValor == null){
			return metodo.invoke(objeto);
		}else{
			return metodo.invoke(objeto, pValor);
		}
	}

	/**
	* Este metodo devuelve un Object[] de 2 posiciones donde la primera posicion corresponde al objeto y el segundo
	* al metodo solicitado del objeto
	* @param pEsMetodoGet
	* @param pObjeto
	* @param pPropiedad
	* @param pParametros
	* @return
	* @throws Exception
	*/
	static final public Object[] obtenerMetodo(Object  pObjeto,
											   String  pPropiedad, 
					 			  		  	   Class   pParametros[]) throws Exception {	
		Method metodo;
		String metodoNombre;
		
		//------------
		StringTokenizer propiedadCompuesta = new StringTokenizer(pPropiedad,".");
		String propiedadToken = (String) propiedadCompuesta.nextElement();
		Object objeto = pObjeto;
		Object objetoCompuesto;
		while(propiedadCompuesta.hasMoreElements()){
			objetoCompuesto = invocarMetodo(objeto, propiedadToken, new Class[] {},"");
			propiedadToken = (String) propiedadCompuesta.nextElement();
			objeto = objetoCompuesto;
		}
		//---------------
		metodoNombre = propiedadToken;
		if (pParametros == null){
			metodo = objeto.getClass().getDeclaredMethod((String) metodoNombre);
		}else {
			metodo = objeto.getClass().getDeclaredMethod((String) metodoNombre, pParametros);
		}
		return new Object[] {objeto,metodo};
	}
	
	static final public Object ejecutarMetodo(Object objeto, String nombreMetodo, Object ... parametros) 
			throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{
		Class [] arregloClases = null;
		if(parametros!=null){
			arregloClases = new Class[parametros.length];
			for (int i = 0; i < parametros.length; i++) {
				arregloClases[i] = parametros[i].getClass();
			}
		}
		
		Method metodo = objeto.getClass().getDeclaredMethod(nombreMetodo, arregloClases);
		Object resultado = metodo.invoke(objeto, parametros);
		return resultado;
	}
	
	static final public Object castearObjeto(Object objetoACastear, Class claseACastear){
		Class clase = objetoACastear.getClass();
		 return clase.cast(objetoACastear);
	}
}
